using System;
using System.Globalization;

namespace QuickBencher
{
    public class Measurement
    {
        internal class Header
        {
            public const string STR_UsingLabels = "UsingLabels";
            public const string STR_NotUsingLabels = "NotUsingLabels";
            public const string STR_LabelWidth = "LabelWidth";

            private int _labelWidth;
            private string _spec;
            
            public static Header UsingLabels = new Header(STR_UsingLabels);
            public static Header NotUsingLabels = new Header(STR_NotUsingLabels);

            public static Header UsingLabelsWidthWidth(int labelWidth)
            {
                return new Header(STR_LabelWidth, labelWidth);
            }

            private Header(string spec)
            {
                _spec = spec;
            }

            private Header(string spec, int labelWidth)
            {
                _spec = spec;
                _labelWidth = labelWidth;
            }
            public int LabelWidth
            {
                get
                {
                    return _labelWidth;
                }
            }
            public string Spec
            {
                get
                {
                    return _spec;
                }
            }
        }

        private const string FIXED_POINT_WITH_SIX_DIGITS = "F6";
        private const int DEFAULT_LABEL_LENGTH = 10;
        private const int TIME_FORMAT_LENGTH = 10;
        private int? _labelWidth;

        private static readonly CultureInfo _formatingCulture = CultureInfo.CreateSpecificCulture("en-US");

        internal Measurement() : this(null) { }

        internal Measurement(int? labelWidth)
        {
            _labelWidth = labelWidth;   
        }

        public string Label { get; internal set; }

        /// <summary>
        /// The amount of time spent running code inside the application portion 
        /// of the process, measured in seconds.
        /// </summary>
        public double UserTime { get; internal set; }
        
        /// <summary>
        /// The amount of time the process has spent running code inside the 
        /// operating system core, measured in seconds.
        /// </summary>
        public double SystemTime { get; internal set; }
        
        /// <summary>
        /// The amount of time that the process has spent utilizing the CPU, 
        /// measured in seconds.
        /// </summary>
        public double TotalTime { get; internal set; }
        
        /// <summary>
        /// The elapsed real time as measured by a Stopwatch (unit of time is 
        /// seconds).
        /// </summary>
        public double RealTime { get; internal set; }

        internal static string GetHeader(Header options)
        {
            string header = string.Empty;

            if (!options.Spec.Equals(Header.STR_NotUsingLabels))
            {
                int labelLength = options.Spec.Equals(Header.STR_LabelWidth) ? options.LabelWidth : DEFAULT_LABEL_LENGTH; // duplicated in ToString()           
                header += string.Empty.PadRight(labelLength);
            }

            header += "user".PadLeft(TIME_FORMAT_LENGTH);
            header += " " + "system".PadLeft(TIME_FORMAT_LENGTH);
            header += " " + "total".PadLeft(TIME_FORMAT_LENGTH);
            header += "  " + "real".PadLeft(TIME_FORMAT_LENGTH);

            return header;
        }

        public override string ToString()
        {
            string formated = string.Empty;

            int labelLength = _labelWidth.HasValue ? _labelWidth.Value : DEFAULT_LABEL_LENGTH; // duplicated in GetHeader()

            if (!string.IsNullOrEmpty(Label))
                formated += Label.PadRight(labelLength);

            formated += string.Format("{0} {1} {2} ({3})", 
                FormatTime(UserTime),
                FormatTime(SystemTime),
                FormatTime(TotalTime),
                FormatTime(RealTime));

            return formated;
        }

        public static string FormatTime(double time)
        {
            return time
                .ToString(FIXED_POINT_WITH_SIX_DIGITS, _formatingCulture)
                .PadLeft(TIME_FORMAT_LENGTH);
        }
    }
}
